#pragma once
#include "session.hpp"
#include "room.hpp"
#include "onlineUsers.hpp"
#include "dbexec.hpp"
#include "logger.hpp"

#include <mutex>
#include <condition_variable> //条件变量
#include <list>
#include <thread>

template <class T>
class Math_Queue
{
private:
    std::list<T> _lt;
    std::mutex _mutex;             // 控制线程安全 多线程
    std::condition_variable _cond; // 条件变量 主要是控制消费者，当队列中的人数 <2 时添加到条件变量下等待
public:
    // 入队数据 唤醒线程
    void push(const T &t)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _lt.push_back(t);
        _cond.notify_all(); // 唤醒所有线程
    }

    // 出队数据
    bool pop(T &t)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        if (_lt.empty() == true)
        {
            return false;
        }
        t = _lt.front();
        _lt.pop_front();
        return true;
    }

    // 移除队列中的数据 根据数值
    void remove(T &t)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _lt.remove(t);
    }

    ssize_t size()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        return _lt.size();
    }

    bool empty()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        return _lt.empty();
    }

    // 阻塞线程
    void wait()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _cond.wait(lock);
    }
};

// 匹配管理
class Matcher
{
private:
    // 队列中保存用户id
    Math_Queue<uint64_t> _q_bronze;
    Math_Queue<uint64_t> _q_diamond;
    Math_Queue<uint64_t> _q_king;
    // 管理队列的线程
    std::thread _th_bronze;
    std::thread _th_diamond;
    std::thread _th_king;
    Room_Manager *_rm;
    OlineUser_Manager *_om;
    user_exec *_ue;

private:
    // 线程对匹配模式处理
    void handle_match(Math_Queue<uint64_t> &mq)
    {
        // 匹配是一个死循环
        while (true)
        {
            // 1、判断队列中是否人数 > 2,是否去条件变量下阻塞等待
            while (mq.size() < 2)
            {
                mq.wait();
            }
            // 2、人数大于2此时，进行出队列操作
            uint64_t uid1, uid2;
            bool res = mq.pop(uid1);
            if (res == false)
            {
                // 此时uid1出队列失败
                continue;
            }
            res = mq.pop(uid2);
            if (res == false)
            {
                // 此时uid2出队失败,将uid1重新添加到队列中
                this->add_math_queue(uid1);
                continue;
            }
            // 3、判断这两个玩家是否在线（是否在大厅中）直接获取玩家的通信连接
            wwserver_t::connection_ptr wcp1 = _om->get_from_hall_con(uid1);
            if (wcp1.get() == nullptr)
            {
                // uid1玩家不在大厅，将uid2玩家重新添加进入队列中
                this->add_math_queue(uid2);
                continue;
            }

            wwserver_t::connection_ptr wcp2 = _om->get_from_hall_con(uid2);
            if (wcp2.get() == nullptr)
            {
                // uid2玩家不在大厅，将uid1玩家重新添加进入队列中
                this->add_math_queue(uid1);
                continue;
            }
            // 4、为这两个玩家创建房间
            room_ptr rp = _rm->create_room(uid1, uid2);
            if (rp.get() == nullptr)
            {
                this->add_math_queue(uid1);
                this->add_math_queue(uid2);
                continue;
            }
            // 5、对这两个玩家进行发送匹配成功通知
            Json::Value value;
            value["optype"] = "match_success";
            value["result"] = true;
            // 进行序列化
            std::string message;
            json_util::serialize(value, message);
            wcp1->send(message);
            wcp2->send(message);
        }
    }

    // 当指定线程人数 >2,从队列中移除用户，并创建房间
    void _th_bronze_entry()
    {
        return handle_match(_q_bronze);
    }

    void _th_diamond_entry()
    {
        return handle_match(_q_diamond);
    }

    void _th_king_entry()
    {
        return handle_match(_q_king);
    }

public:
    Matcher(Room_Manager *rm, OlineUser_Manager *om, user_exec *ue)
        : _rm(rm), _om(om), _ue(ue),
          _th_bronze(std::thread(&Matcher::_th_bronze_entry, this)), // 默认有个参数this
          _th_diamond(std::thread(&Matcher::_th_diamond_entry, this)),
          _th_king(std::thread(&Matcher::_th_king_entry, this))
    {
        DLOG("游戏匹配模块初始化完毕!!!");
    }
    // 添加用户到匹配队列中
    bool add_math_queue(uint64_t uid)
    {
        // 根据不同的天梯分数，分配不同的队列
        // 1、根据用户的ID获取到对应的用户信息
        Json::Value user;
        bool ret = _ue->select_by_id(uid, user);
        if (ret == false)
        {
            DLOG("获取玩家: %d 用户信息失败!");
            return false;
        }
        // 2、添加到对应等级的队列中
        int score = user["score"].asUInt64();
        if (score < 3000)
        {
            _q_bronze.push(uid);
        }
        else if (score >= 3000 && score <= 5000)
        {
            _q_diamond.push(uid);
        }
        else
        {
            _q_king.push(uid);
        }
        return true;
    }

    // 将用户从匹配队列中删除
    bool del_math_queue(uint64_t uid)
    {
        // 根据不同的天梯分数，分配不同的队列
        // 1、根据用户的ID获取到对应的用户信息
        Json::Value user;
        bool ret = _ue->select_by_id(uid, user);
        if (ret == false)
        {
            DLOG("获取玩家: %d 用户信息失败!");
            return false;
        }
        // 2、添加到对应等级的队列中
        int score = user["score"].asUInt64();
        if (score < 3000)
        {
            _q_bronze.remove(uid);
        }
        else if (score >= 3000 && score <= 5000)
        {
            _q_diamond.remove(uid);
        }
        else
        {
            _q_king.remove(uid);
        }
        return true;
    }
};